Introduction to do {... } while of 0 in macro definitions

  • 2020-05-12 02:53:04
  • OfStack

If you're an C programmer, you're familiar with macros, which are powerful and, when used correctly, can make your job a lot easier. However, if you define macros casually and without checking them carefully, they can drive you crazy and waste N's time. In many C programs, you may see special macro definitions that don't seem straightforward.

Here's an example:


#define __set_task_state(tsk, state_value)   \ 
  do { (tsk)->state = (state_value); } while (0) 

Many of the Linux kernel and other well-known C libraries use do{... }while(0) macro definition. What is the purpose of this macro? What are the benefits?

Robert Love (previously engaged in Linux kernel development) gives us the following answers:

do {... }while(0) is a one-only constructor in C that lets you define macros that always work the same way, so that no matter how you use macros (especially if you don't surround them with braces), the semicolon after the macro will work the same way.

This may sound like a mouthful, but it can be summed up in one sentence: use do{... }while(0) macro definitions are not affected by curly braces, semicolons, etc., and will always run the way you expect.

Such as:


#define foo(x) bar(x); baz(x) 

Then you might call:


foo(wolf); 

This will be extended by the macro to:


bar(wolf); baz(wolf); 

This is indeed the correct output we expect. Let's see if we call it like this:


if (!feral) 
foo(wolf); 

Then the extension may not be what you expect. The above statement will be extended to:


if (!feral) 
bar(wolf); 
baz(wolf); 

Obviously, this is a mistake, and it's one of the most common mistakes people make.


In almost all cases, it is impossible to expect to write multiple statement macros to achieve the correct result. You can't have macros behave like function 1-without do/while(0).

If we use do{... }while(0) to redefine the macro, that is:


#define foo(x) do { bar(x); baz(x); } while (0) 

Now, the statement is functionally equivalent to the former, do ensures that the logic in the curly braces is executed, while while(0) ensures that the logic is executed only once, just as it would be if there were no loops.

For the if statement above, it will be extended to:


if (!feral) 
do { bar(wolf); baz(wolf); } while (0); 

Semantically, it is equivalent to the following statement:


if (!feral) { 
  bar(wolf); 
  baz(wolf); 
} 

This might be confusing to you, but why not wrap the macro in curly braces? Why do you have to use do/while(0) logic?

For example, we use curly braces to define macros like this:


#define foo(x) { bar(x); baz(x); } 

This is true for the if statement shown above, but what if we had the following statement called:


#define foo(x) bar(x); baz(x) 
0

The macro expands to:


#define foo(x) bar(x); baz(x) 
1

As you can see, this is a grammatical error.

Summary: macros in Linux and other libraries use do/while(0) to surround the execution logic, because it ensures that the macro will always behave the same, regardless of how many semicolons and braces are used in the calling code.


Related articles: